package cn.tedu.csmall.product.config;

import cn.tedu.csmall.product.filter.JwtAuthorizationFilter;
import cn.tedu.csmall.product.web.JsonResult;
import cn.tedu.csmall.product.web.ServiceCode;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.io.PrintWriter;

/**
 * Spring Security的配置类
 *
 * @author Chen
 * @since 2023/2/6 21:24:24
 */
@Slf4j
@Configuration
@EnableMethodSecurity
public class SecurityConfiguration {

    private final JwtAuthorizationFilter jwtAuthorizationFilter;

    @Autowired
    public SecurityConfiguration(JwtAuthorizationFilter jwtAuthorizationFilter) {
        log.debug("创建配置类对象：SecurityConfiguration");
        this.jwtAuthorizationFilter = jwtAuthorizationFilter;
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        String[] urls = {
                "/doc.html",
                "/*/*.js",
                "/*/*.css",
                "/swagger-ui.html",
                "/v3/api-docs"
        };
        return http
                // 处理“需要通过认证，但是实际上未通过认证就发起的请求”导致的错误
                .exceptionHandling(conf -> conf.authenticationEntryPoint((request, response, e) -> {
                    String message = "未检测到登录，请登录！";
                    JsonResult<Void> jsonResult = JsonResult.fail(ServiceCode.ERR_UNAUTHORIZED, message);
                    response.setContentType("application/json; charset=utf-8");
                    PrintWriter printWriter = response.getWriter();
                    printWriter.println(JSON.toJSONString(jsonResult));
                    printWriter.close();
                }))
                // 解决复杂请求的预检机制导致的跨域问题
                .cors(conf -> conf.configure(http))
                // CSRF (Cross-Site Request Forgery) 禁用“防止伪造的跨域攻击”这种防御机制
                .csrf(AbstractHttpConfigurer::disable)
                // 配置Session的管理为“无状态的”
                .sessionManagement(conf -> conf.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                // 配置URL的访问控制
                // 放行所有的OPTIONS类型的请求
                .authorizeHttpRequests(auth -> auth.requestMatchers(HttpMethod.OPTIONS, "/*").permitAll())
                // 匹配某些URL，允许匿名访问
                .authorizeHttpRequests(auth -> auth.requestMatchers(urls).anonymous())
                // 其他请求放行
                .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
                .addFilterBefore(jwtAuthorizationFilter, UsernamePasswordAuthenticationFilter.class)
                // 调用formLogin()表示启用登录表单页和登出页
                // 如果未调用此方法，则没有登录表单页和登出页，且，当视为“未通过认证时”，将响应403
                .formLogin(conf -> {
                    try {
                        conf.init(http);
                    } catch (Exception e) {
                        log.error("抛出异常：{}", e.getMessage());
                    }
                })
                .build();
    }
}
